/*******************************************************************************
 * Copyright 2009 DCSpectrometer - http://code.google.com/p/dcspectrometer 
 *  
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *     
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 *******************************************************************************/
package com.dcspectrometer;

import java.awt.image.Raster;

import com.dcspectrometer.Analysis.IStatCalculator;
import com.dcspectrometer.Analysis.Maximum;
import com.dcspectrometer.Analysis.Mean;
import com.dcspectrometer.Analysis.Minimum;
import com.dcspectrometer.Analysis.StandardDeviation;

public class RasterTileStatistics {
  /** Number of channels */
  private int channelCount;
  /** Per channel min */
  private Minimum[] min;
  /** Per channel max */
  private Maximum[] max;
  /** Per channel mean */
  private Mean[] mean;
  /** Per channel standard deviation */
  private StandardDeviation[] standardDeviation;
  /** raster width */
  private int width;
  /** raster width */
  private int height;
  /** x coordinate of the upper left corner */
  private int x;
  /** y coordinate of the upper left corner */
  private int y;

  public RasterTileStatistics(Raster raster) throws IllegalArgumentException {
    if (raster == null) {
      throw new IllegalArgumentException("Image is null.");
    }

    if (raster.getWidth() < 1 || raster.getHeight() < 1) {
      throw new IllegalArgumentException("Image must have at least 1 pixel.");
    }

    this.x = raster.getMinX();
    this.y = raster.getMinY();
    this.height = raster.getHeight();
    this.width = raster.getWidth();
    this.channelCount = raster.getNumBands();

    min = new Minimum[channelCount];
    max = new Maximum[channelCount];
    mean = new Mean[channelCount];
    standardDeviation = new StandardDeviation[channelCount];

    for (int i = 0; i < channelCount; i++) {
      min[i] = new Minimum();
      max[i] = new Maximum();
      mean[i] = new Mean();
      standardDeviation[i] = new StandardDeviation();
    }

    int pixel[] = new int[channelCount];

    for (int i = 0; i < width; i++) {
      for (int j = 0; j < height; j++) {
        raster.getPixel(x + i, y + j, pixel);

        for (int k = 0; k < channelCount; k++) {
          min[k].addValue(pixel[k]);
          max[k].addValue(pixel[k]);
          mean[k].addValue(pixel[k]);
          standardDeviation[k].addValue(pixel[k]);
        }
      }
    }
  }

  public RasterTileStatistics(Raster raster, double average[], double range[]) throws IllegalArgumentException {
    if (raster == null) {
      throw new IllegalArgumentException("Image is null.");
    }

    if (raster.getWidth() < 1 || raster.getHeight() < 1) {
      throw new IllegalArgumentException("Image must have at least 1 pixel.");
    }

    this.x = raster.getMinX();
    this.y = raster.getMinY();
    this.height = raster.getHeight();
    this.width = raster.getWidth();
    this.channelCount = raster.getNumBands();

    min = new Minimum[channelCount];
    max = new Maximum[channelCount];
    mean = new Mean[channelCount];
    standardDeviation = new StandardDeviation[channelCount];

    for (int i = 0; i < channelCount; i++) {
      min[i] = new Minimum();
      max[i] = new Maximum();
      mean[i] = new Mean();
      standardDeviation[i] = new StandardDeviation();
    }

    int pixel[] = new int[channelCount];

    for (int i = 0; i < width; i++) {
      for (int j = 0; j < height; j++) {
        raster.getPixel(x + i, y + j, pixel);

        boolean addPoint;
        addPoint = false;
        
        for (int k = 0; k < channelCount; k++) {
          if (Math.abs(pixel[k] - average[k]) < range[k]) {
            addPoint = true;
          }
        }
        
        if (addPoint) {
        for (int k = 0; k < channelCount; k++) {
          min[k].addValue(pixel[k]);
          max[k].addValue(pixel[k]);
          mean[k].addValue(pixel[k]);
          standardDeviation[k].addValue(pixel[k]);
        }
        }
      }
    }
  }
  
  /** @return the channelCount */
  public int getChannelCount() {
    return channelCount;
  }

  /** @return the mean */
  public double[] getMean() {
    return getCalculatorValues(mean);
  }

  /** @return the standardDeviation */
  public double[] getStandardDeviation() {
    return getCalculatorValues(standardDeviation);
  }

  /** @return the width */
  public int getWidth() {
    return width;
  }

  /** @return the height */
  public int getHeight() {
    return height;
  }

  /** @return the x */
  public int getX() {
    return x;
  }

  /** @return the y */
  public int getY() {
    return y;
  }

  /** @return the min */
  public double[] getMin() {
    return getCalculatorValues(min);
  }

  /** @return the max */
  public double[] getMax() {
    return getCalculatorValues(max);
  }

  private double[] getCalculatorValues(IStatCalculator[] calculator) {
    double[] calculatorValues = new double[channelCount];

    for (int i = 0; i < channelCount; i++) {
      calculatorValues[i] = calculator[i].getResults();
    }

    return calculatorValues;
  }

  public String toString(int channelIndex, String channelName) {
    return channelName + ": min = " + min[channelIndex].getResults() + ", max = "
        + max[channelIndex].getResults() + ", mean = " + mean[channelIndex].getResults()
        + ", stdDiv = " + standardDeviation[channelIndex].getResults();
  }

  public static String getTileInfo(RasterTileStatistics stats) {
    return getTileChannelInfo(stats, "R", 0) + " " + getTileChannelInfo(stats, "G", 1) + " "
        + getTileChannelInfo(stats, "B", 2);
  }

  private static String getTileChannelInfo(RasterTileStatistics stats, String channelLabel,
      int channelIndex) {
    String info = "";

    double[] min = stats.getMin();
    double[] max = stats.getMax();
    double[] mean = stats.getMean();
    double[] std = stats.getStandardDeviation();

    info = String.format("(%s, %5d, %5d, %5d, %5d)", channelLabel, (int) min[channelIndex],
        (int) max[channelIndex], (int)mean[channelIndex], (int)std[channelIndex]);
    return info;
  }

}
